/**
   * Handle all necessary tasks that can be delayed until initialization completes.
   *
   * @param activityCreationTimeMs The time of creation for the activity this toolbar belongs to.
   * @param activityName Simple class name for the activity this toolbar belongs to.
   */
  public void onDeferredStartup(final long activityCreationTimeMs, final String activityName) {
    // Record startup performance statistics
    long elapsedTime = SystemClock.elapsedRealtime() - activityCreationTimeMs;
    if (elapsedTime < RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS) {
      ThreadUtils.postOnUiThreadDelayed(
          new Runnable() {
            @Override
            public void run() {
              onDeferredStartup(activityCreationTimeMs, activityName);
            }
          },
          RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS - elapsedTime);
    }
    RecordHistogram.recordTimesHistogram(
        "MobileStartup.ToolbarFirstDrawTime." + activityName,
        mToolbar.getFirstDrawTime() - activityCreationTimeMs,
        TimeUnit.MILLISECONDS);

    long firstFocusTime = mToolbar.getLocationBar().getFirstUrlBarFocusTime();
    if (firstFocusTime != 0) {
      RecordHistogram.recordCustomTimesHistogram(
          "MobileStartup.ToolbarFirstFocusTime." + activityName,
          firstFocusTime - activityCreationTimeMs,
          MIN_FOCUS_TIME_FOR_UMA_HISTOGRAM_MS,
          MAX_FOCUS_TIME_FOR_UMA_HISTOGRAM_MS,
          TimeUnit.MILLISECONDS,
          50);
    }
  }
 /**
  * Focuses or unfocuses the URL bar.
  *
  * @param focused Whether URL bar should be focused.
  */
 public void setUrlBarFocus(boolean focused) {
   if (!isInitialized()) return;
   mToolbar.getLocationBar().setUrlBarFocus(focused);
 }
  /**
   * Initialize the manager with the components that had native initialization dependencies.
   *
   * <p>Calling this must occur after the native library have completely loaded.
   *
   * @param tabModelSelector The selector that handles tab management.
   * @param fullscreenManager The manager in charge of interacting with the fullscreen feature.
   * @param findToolbarManager The manager for find in page.
   * @param overviewModeBehavior The overview mode manager.
   * @param layoutDriver A {@link LayoutManager} instance used to watch for scene changes.
   */
  public void initializeWithNative(
      TabModelSelector tabModelSelector,
      ChromeFullscreenManager fullscreenManager,
      final FindToolbarManager findToolbarManager,
      final OverviewModeBehavior overviewModeBehavior,
      final LayoutManager layoutDriver,
      OnClickListener tabSwitcherClickHandler,
      OnClickListener newTabClickHandler,
      OnClickListener bookmarkClickHandler,
      OnClickListener customTabsBackClickHandler) {
    assert !mInitializedWithNative;
    mTabModelSelector = tabModelSelector;

    mToolbar.getLocationBar().updateVisualsForState();
    mToolbar.getLocationBar().setUrlToPageUrl();
    mToolbar.setOnTabSwitcherClickHandler(tabSwitcherClickHandler);
    mToolbar.setOnNewTabClickHandler(newTabClickHandler);
    mToolbar.setBookmarkClickHandler(bookmarkClickHandler);
    mToolbar.setCustomTabCloseClickHandler(customTabsBackClickHandler);

    mToolbarModel.initializeWithNative();

    mToolbar.addOnAttachStateChangeListener(
        new OnAttachStateChangeListener() {
          @Override
          public void onViewDetachedFromWindow(View v) {
            Context context = mToolbar.getContext();
            HomepageManager.getInstance(context).removeListener(mHomepageStateListener);
            mTabModelSelector.removeObserver(mTabModelSelectorObserver);
            for (TabModel model : mTabModelSelector.getModels()) {
              model.removeObserver(mTabModelObserver);
            }
            if (mBookmarksBridge != null) {
              mBookmarksBridge.destroy();
              mBookmarksBridge = null;
            }
            if (mTemplateUrlObserver != null) {
              TemplateUrlService.getInstance().removeObserver(mTemplateUrlObserver);
              mTemplateUrlObserver = null;
            }

            findToolbarManager.removeObserver(mFindToolbarObserver);
            if (overviewModeBehavior != null) {
              overviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
            }
            if (layoutDriver != null) {
              layoutDriver.removeSceneChangeObserver(mSceneChangeObserver);
            }
          }

          @Override
          public void onViewAttachedToWindow(View v) {
            // As we have only just registered for notifications, any that were sent prior to
            // this may have been missed.
            // Calling refreshSelectedTab in case we missed the initial selection notification.
            refreshSelectedTab();
          }
        });

    mFindToolbarManager = findToolbarManager;

    assert fullscreenManager != null;
    mFullscreenManager = fullscreenManager;

    mNativeLibraryReady = false;

    findToolbarManager.addObserver(mFindToolbarObserver);
    if (overviewModeBehavior != null) {
      overviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
    }
    if (layoutDriver != null) layoutDriver.addSceneChangeObserver(mSceneChangeObserver);

    onNativeLibraryReady();
    mInitializedWithNative = true;
  }
 /** @return The view that the pop up menu should be anchored to on the UI. */
 public View getMenuAnchor() {
   return mToolbar.getLocationBar().getMenuAnchor();
 }
  /**
   * Creates a ToolbarManager object.
   *
   * @param controlContainer The container of the toolbar.
   * @param menuHandler The handler for interacting with the menu.
   */
  public ToolbarManager(
      final ChromeActivity activity,
      ToolbarControlContainer controlContainer,
      final AppMenuHandler menuHandler,
      final ChromeAppMenuPropertiesDelegate appMenuPropertiesDelegate,
      Invalidator invalidator) {
    mActionBarDelegate =
        new ContextualMenuBar.ActionBarDelegate() {
          @Override
          public void setControlTopMargin(int margin) {
            FrameLayout.LayoutParams lp =
                (FrameLayout.LayoutParams) mControlContainer.getLayoutParams();
            lp.topMargin = margin;
            mControlContainer.setLayoutParams(lp);
          }

          @Override
          public int getControlTopMargin() {
            FrameLayout.LayoutParams lp =
                (FrameLayout.LayoutParams) mControlContainer.getLayoutParams();
            return lp.topMargin;
          }

          @Override
          public ActionBar getSupportActionBar() {
            return activity.getSupportActionBar();
          }

          @Override
          public void setActionBarBackgroundVisibility(boolean visible) {
            int visibility = visible ? View.VISIBLE : View.GONE;
            activity.findViewById(R.id.action_bar_black_background).setVisibility(visibility);
            // TODO(tedchoc): Add support for changing the color based on the brand color.
          }
        };

    mToolbarModel = new ToolbarModelImpl();
    mControlContainer = controlContainer;
    assert mControlContainer != null;

    mToolbar = (ToolbarLayout) controlContainer.findViewById(R.id.toolbar);

    mToolbar.setPaintInvalidator(invalidator);

    mContextualMenuBar = new ContextualMenuBar(activity, mActionBarDelegate);
    mContextualMenuBar.setCustomSelectionActionModeCallback(
        new CustomSelectionActionModeCallback());
    mContextualMenuBar.setTabStripHeight(mToolbar.getTabStripHeight());

    MenuDelegatePhone menuDelegate =
        new MenuDelegatePhone() {
          @Override
          public void updateReloadButtonState(boolean isLoading) {
            if (appMenuPropertiesDelegate != null) {
              appMenuPropertiesDelegate.loadingStateChanged(isLoading);
              menuHandler.menuItemContentChanged(R.id.icon_row_menu_id);
            }
          }
        };
    setMenuDelegatePhone(menuDelegate);

    mLocationBar = mToolbar.getLocationBar();
    mLocationBar.setToolbarDataProvider(mToolbarModel);
    mLocationBar.setUrlFocusChangeListener(this);
    mLocationBar.setDefaultTextEditActionModeCallback(
        mContextualMenuBar.getCustomSelectionActionModeCallback());
    mLocationBar.initializeControls(
        new WindowDelegate(activity.getWindow()),
        mContextualMenuBar.getActionBarDelegate(),
        activity.getWindowAndroid());
    mLocationBar.setIgnoreURLBarModification(false);

    setMenuHandler(menuHandler);
    mToolbar.initialize(mToolbarModel, this, mAppMenuButtonHelper);

    mHomepageStateListener =
        new HomepageStateListener() {
          @Override
          public void onHomepageStateUpdated() {
            mToolbar.onHomeButtonUpdate(HomepageManager.isHomepageEnabled(mToolbar.getContext()));
          }
        };
    HomepageManager.getInstance(mToolbar.getContext()).addListener(mHomepageStateListener);

    mTabModelSelectorObserver =
        new EmptyTabModelSelectorObserver() {
          @Override
          public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
            refreshSelectedTab();
            updateTabCount();
            mControlContainer.invalidateBitmap();
          }

          @Override
          public void onTabStateInitialized() {
            mTabRestoreCompleted = true;
            handleTabRestoreCompleted();
          }
        };

    mTabModelObserver =
        new EmptyTabModelObserver() {
          @Override
          public void didAddTab(Tab tab, TabLaunchType type) {
            updateTabCount();
          }

          @Override
          public void didSelectTab(Tab tab, TabSelectionType type, int lastId) {
            mPreselectedTabId = Tab.INVALID_TAB_ID;
            refreshSelectedTab();
          }

          @Override
          public void tabClosureUndone(Tab tab) {
            updateTabCount();
            refreshSelectedTab();
          }

          @Override
          public void didCloseTab(Tab tab) {
            updateTabCount();
            refreshSelectedTab();
          }

          @Override
          public void tabPendingClosure(Tab tab) {
            updateTabCount();
            refreshSelectedTab();
          }

          @Override
          public void allTabsPendingClosure(List<Integer> tabIds) {
            updateTabCount();
            refreshSelectedTab();
          }
        };

    mTabObserver =
        new EmptyTabObserver() {
          @Override
          public void onSSLStateUpdated(Tab tab) {
            super.onSSLStateUpdated(tab);
            assert tab == mToolbarModel.getTab();
            mLocationBar.updateSecurityIcon(tab.getSecurityLevel());
          }

          @Override
          public void onWebContentsInstantSupportDisabled() {
            mLocationBar.setUrlToPageUrl();
          }

          @Override
          public void onDidNavigateMainFrame(
              Tab tab,
              String url,
              String baseUrl,
              boolean isNavigationToDifferentPage,
              boolean isFragmentNavigation,
              int statusCode) {
            if (isNavigationToDifferentPage) {
              mToolbar.onNavigatedToDifferentPage();
            }
          }

          @Override
          public void onPageLoadStarted(Tab tab, String url) {
            updateButtonStatus();
            updateTabLoadingState(true, true);
          }

          @Override
          public void onPageLoadFinished(Tab tab) {
            ToolbarManager.this.onPageLoadFinished();
          }

          @Override
          public void onPageLoadFailed(Tab tab, int errorCode) {
            ToolbarManager.this.onPageLoadFailed();
          }

          @Override
          public void onTitleUpdated(Tab tab) {
            mLocationBar.setTitleToPageTitle();
          }

          @Override
          public void onUrlUpdated(Tab tab) {
            // Update the SSL security state as a result of this notification as it will
            // sometimes be the only update we receive.
            updateTabLoadingState(false, true);

            // A URL update is a decent enough indicator that the toolbar widget is in
            // a stable state to capture its bitmap for use in fullscreen.
            mControlContainer.setReadyForBitmapCapture(true);
          }

          @Override
          public void onCrash(Tab tab, boolean sadTabShown) {
            onTabCrash();
          }

          @Override
          public void onLoadProgressChanged(Tab tab, int progress) {
            updateLoadProgress(progress);
          }

          @Override
          public void onContentChanged(Tab tab) {
            mToolbar.onTabContentViewChanged();
          }

          @Override
          public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) {
            if (!didStartLoad) return;

            ChromeTab chromeTab = ChromeTab.fromTab(tab);
            if (!chromeTab.getBackgroundContentViewHelper().isPageSwappingInProgress()
                && didFinishLoad) {
              mLoadProgressSimulator.start();
            }
          }

          @Override
          public void onDidStartNavigationToPendingEntry(Tab tab, String url) {
            // Update URL as soon as it becomes available when it's a new tab.
            // But we want to update only when it's a new tab. So we check whether the current
            // navigation entry is initial, meaning whether it has the same target URL as the
            // initial URL of the tab.
            WebContents webContents = tab.getWebContents();
            if (webContents == null) return;
            NavigationController navigationController = webContents.getNavigationController();
            if (navigationController == null) return;
            if (navigationController.isInitialNavigation()) {
              mLocationBar.setUrlToPageUrl();
            }
          }

          @Override
          public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) {
            NewTabPage ntp = mToolbarModel.getNewTabPageForCurrentTab();
            if (ntp == null) return;
            if (!NewTabPage.isNTPUrl(params.getUrl())
                && loadType != TabLoadStatus.PAGE_LOAD_FAILED) {
              ntp.setUrlFocusAnimationsDisabled(true);
              mToolbar.onTabOrModelChanged();
            }
          }

          @Override
          public void onDidFailLoad(
              Tab tab,
              boolean isProvisionalLoad,
              boolean isMainFrame,
              int errorCode,
              String description,
              String failingUrl) {
            NewTabPage ntp = mToolbarModel.getNewTabPageForCurrentTab();
            if (ntp == null) return;
            if (isProvisionalLoad && isMainFrame) {
              ntp.setUrlFocusAnimationsDisabled(false);
              mToolbar.onTabOrModelChanged();
            }
          }

          @Override
          public void onContextualActionBarVisibilityChanged(Tab tab, boolean visible) {
            if (visible) RecordUserAction.record("MobileActionBarShown");
            ActionBar actionBar = mActionBarDelegate.getSupportActionBar();
            if (!visible && actionBar != null) actionBar.hide();
            if (DeviceFormFactor.isTablet(activity)) {
              if (visible) {
                mContextualMenuBar.showControls();
              } else {
                mContextualMenuBar.hideControls();
              }
            }
          }
        };

    mBookmarksObserver =
        new BookmarksBridge.BookmarkModelObserver() {
          @Override
          public void bookmarkModelChanged() {
            updateBookmarkButtonStatus();
          }
        };

    mFindToolbarObserver =
        new FindToolbarObserver() {
          @Override
          public void onFindToolbarShown() {
            mToolbar.handleFindToolbarStateChange(true);
            if (mFullscreenManager != null) {
              mFullscreenFindInPageToken =
                  mFullscreenManager.showControlsPersistentAndClearOldToken(
                      mFullscreenFindInPageToken);
            }
          }

          @Override
          public void onFindToolbarHidden() {
            mToolbar.handleFindToolbarStateChange(false);
            if (mFullscreenManager != null) {
              mFullscreenManager.hideControlsPersistent(mFullscreenFindInPageToken);
              mFullscreenFindInPageToken = FullscreenManager.INVALID_TOKEN;
            }
          }
        };

    mOverviewModeObserver =
        new EmptyOverviewModeObserver() {
          @Override
          public void onOverviewModeStartedShowing(boolean showToolbar) {
            mToolbar.setTabSwitcherMode(true, showToolbar, false);
            updateButtonStatus();
          }

          @Override
          public void onOverviewModeStartedHiding(boolean showToolbar, boolean delayAnimation) {
            mToolbar.setTabSwitcherMode(false, showToolbar, delayAnimation);
            updateButtonStatus();
          }

          @Override
          public void onOverviewModeFinishedHiding() {
            mToolbar.onTabSwitcherTransitionFinished();
          }
        };

    mSceneChangeObserver =
        new SceneChangeObserver() {
          @Override
          public void onTabSelectionHinted(int tabId) {
            mPreselectedTabId = tabId;
            refreshSelectedTab();
          }

          @Override
          public void onSceneChange(Layout layout) {
            mToolbar.setContentAttached(layout.shouldDisplayContentOverlay());
          }
        };

    mLoadProgressSimulator = new LoadProgressSimulator(this);
  }